home *** CD-ROM | disk | FTP | other *** search
/ Experimental BBS Explossion 3 / Experimental BBS Explossion III.iso / c / tde31.zip / DIRLIST.C < prev    next >
C/C++ Source or Header  |  1993-08-29  |  31KB  |  948 lines

  1. /*
  2.  * I wrote this function because I'm so stupid, I constantly forget
  3.  *  file names and directory stuff.  The main function prompts for a
  4.  *  subdirectory name or a search path.  The default search path is the
  5.  *  cwd (current working directory).  In addition to being stupid, I'm also
  6.  *  lazy.  If the user types a subdirectory name, I think we can assume he
  7.  *  wants to list all files w/o having to type *.*   Let's save the cwd on
  8.  *  whatever drive the user wishes to search, so we can restore it we get
  9.  *  thru dir'ing.  Use the standard DOS functions to get and set directories.
  10.  *
  11.  * The search pattern can contain wild card chars, valid file names, or a
  12.  *  valid subdirectory name.
  13.  *
  14.  * Being that TDE 2.2 now handles binary files, lets make .EXE and .COM files
  15.  *  autoload in binary mode.
  16.  *
  17.  * Before matching files are displayed on the screen, file names are sorted
  18.  *  using the easy-to-implement and fairly fast Shellsort algorithm.
  19.  *
  20.  * See:
  21.  *
  22.  *   Donald Lewis Shell, "A High-Speed Sorting Procedure."  _Communications of
  23.  *     the ACM_ 2 (No. 2): 30-32, 1959.
  24.  *
  25.  * See also:
  26.  *
  27.  *   Donald Ervin Knuth, _The Art of Computer Programming; Volume 3:  Sorting
  28.  *     and Searching_, Addison-Wesley, Reading, Mass., 1973, Chapter 5,
  29.  *     "Sorting", pp 84-95.  ISBN 0-201-03803-X.
  30.  *
  31.  *   Robert Sedgewick, _Algorithms in C_, Addison-Wesley, Reading, Mass.,
  32.  *     1990, Chapter 8, "Elementary Sorting Methods", pp 107-111.
  33.  *     ISBN 0-201-51425-7.
  34.  *
  35.  *
  36.  * New editor name:  TDE, the Thomson-Davis Editor.
  37.  * Author:           Frank Davis
  38.  * Date:             June 5, 1991, version 1.0
  39.  * Date:             July 29, 1991, version 1.1
  40.  * Date:             October 5, 1991, version 1.2
  41.  * Date:             January 20, 1992, version 1.3
  42.  * Date:             February 17, 1992, version 1.4
  43.  * Date:             April 1, 1992, version 1.5
  44.  * Date:             June 5, 1992, version 2.0
  45.  * Date:             October 31, 1992, version 2.1
  46.  * Date:             April 1, 1993, version 2.2
  47.  * Date:             June 5, 1993, version 3.0
  48.  * Date:             August 29, 1993, version 3.1
  49.  *
  50.  * This code is released into the public domain, Frank Davis.
  51.  *    You may distribute it freely.
  52.  */
  53.  
  54. #include "tdestr.h"
  55. #include "common.h"
  56. #include "define.h"
  57. #include "tdefunc.h"
  58.  
  59.  
  60. /*
  61.  * Name:    dir_help
  62.  * Purpose: To prompt the user and list the directory contents
  63.  * Date:    February 13, 1992
  64.  * Passed:  window:  pointer to current window
  65.  */
  66. int  dir_help( WINDOW *window )
  67. {
  68. char dname[MAX_COLS+2]; /* directory search pattern */
  69. char stem[MAX_COLS+2];  /* directory stem */
  70. char drive[_MAX_DRIVE]; /* splitpath drive buff */
  71. char dir[_MAX_DIR];     /* splitpath dir buff */
  72. char fname[_MAX_FNAME]; /* splitpath fname buff */
  73. char ext[_MAX_EXT];     /* splitpath ext buff */
  74. int  rc;
  75. int  file_mode;
  76. int  bin_length;
  77. int  prompt_line;
  78.  
  79.    if (window != NULL) {
  80.       entab_linebuff( );
  81.       if (un_copy_line( window->ll, window, TRUE ) == ERROR)
  82.          return( ERROR );
  83.       prompt_line = window->bottom_line;
  84.    } else
  85.       prompt_line = g_display.nlines;
  86.  
  87.    /*
  88.     * search path or pattern
  89.     */
  90.    dname[0] = '\0';
  91.    rc = get_name( dir1, prompt_line, dname, g_display.message_color );
  92.  
  93.    if (rc == OK) {
  94.       if (validate_path( dname, stem ) == OK) {
  95.          rc = list_and_pick( dname, stem, window );
  96.  
  97.          /*
  98.           * if everything is everything, load in the file selected by user.
  99.           */
  100.          if (rc == OK) {
  101.             file_mode = TEXT;
  102.             bin_length = 0;
  103.             _splitpath( dname, drive, dir, fname, ext );
  104.             if (stricmp( ext, ".exe" ) == 0  ||  stricmp( ext, ".com" ) == 0) {
  105.                file_mode = BINARY;
  106.                bin_length = g_status.file_chunk;
  107.             }
  108.             if (window != NULL)
  109.                attempt_edit_display( dname, LOCAL, file_mode, bin_length );
  110.             else
  111.                attempt_edit_display( dname, GLOBAL, file_mode, bin_length );
  112.          }
  113.       } else
  114.          /*
  115.           * invalid path or file name
  116.           */
  117.          error( WARNING,
  118.                 window != NULL ? window->bottom_line : g_display.nlines, dir2 );
  119.    }
  120.    return( rc );
  121. }
  122.  
  123.  
  124. /*
  125.  * Name:    validate_path
  126.  * Purpose: make sure we got a valid search pattern or subdirectory
  127.  * Date:    February 13, 1992
  128.  * Passed:  dname: search path entered by user
  129.  *          stem:  directory stem is returned
  130.  * Returns: successful or not
  131.  * Notes:   we need to validate the search path or pattern.  if the search
  132.  *            pattern is valid, then we need to get the search stem.
  133.  *          the user may enter a subdirectory or some kind of search pattern.
  134.  *             if the user enters a subdirectory, then there are a few things
  135.  *             we need to take care of  1) find out if the subdirectory is
  136.  *             the root, 2) append a '\' to the subdirectory so we can create
  137.  *             a search pattern for the subdirectory, 3) don't append '\' to
  138.  *             the root, it already has a '\'.
  139.  *          if the user enters a search pattern, then we need to dissect the
  140.  *             search path.  we must create a stem from the search pattern.
  141.  */
  142. int  validate_path( char *dname, char *stem )
  143. {
  144. int  rc;
  145. DTA  dta;               /* temp disk transfer struct for findfirst, etc. */
  146. int  fattr;
  147. int  i;
  148. int  len;
  149. char *p;
  150. char temp[MAX_COLS+2];  /* directory stem */
  151.  
  152.    /*
  153.     * if path name is void then the current working directory is implied.
  154.     */
  155.    if (dname[0] == '\0') {
  156.  
  157.       assert( strlen( stardotstar ) < MAX_COLS );
  158.  
  159.       strcpy( dname, stardotstar );
  160.       stem[0] = '\0';
  161.       rc = OK;
  162.    } else {
  163.  
  164.       /*
  165.        * get the attributes of the search pattern, so we can determine if
  166.        * this is a pattern or subdirectory.
  167.        */
  168.       rc = get_fattr( dname, &fattr );
  169.  
  170.       if (rc == OK && (fattr & SUBDIRECTORY)) {
  171.          assert( strlen( dname ) < MAX_COLS );
  172.          strcpy( stem, dname );
  173.  
  174.          /*
  175.           * if this is the root directory ( \ ), don't append '\' to it.
  176.           * user entered a subdirectory - append *.* to get contents of
  177.           * subdirectory.
  178.           */
  179.          len = strlen( stem );
  180.          if (stem[len-1] != '\\') {
  181.             strcat( stem, "\\" );
  182.             strcat( dname, "\\" );
  183.          }
  184.          strcat( dname, stardotstar );
  185.  
  186.       /*
  187.        * not a subdirectory.  let's see if any files match the search
  188.        * pattern.
  189.        */
  190.       } else if (rc != ERROR) {
  191.          if ((rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN |
  192.                               SYSTEM | SUBDIRECTORY | ARCHIVE )) == OK) {
  193.  
  194.             /*
  195.              * copy dname to "temp" so we can use "temp" to find the stem.
  196.              *    we need to search the pattern backwards to figure the stem.
  197.              */
  198.  
  199.             assert( strlen( dname ) < MAX_COLS );
  200.  
  201.             strcpy( temp, dname );
  202.             len = strlen( dname );
  203.             for (i=len,p=temp+len; i>=0; i--) {
  204.                /*
  205.                 *  if we run into the '\' or the ':', then we got a stem.
  206.                 */
  207.                if (*p == '\\' || *p == ':') {
  208.                   p = temp + i;
  209.                   *(p+1) = '\0';
  210.                   break;
  211.                /*
  212.                 * if we're at the beginning of the string, stem == '\0'
  213.                 */
  214.                } else if (i == 0) {
  215.                   *p = '\0';
  216.                   break;
  217.                }
  218.                --p;
  219.             }
  220.  
  221.             assert( strlen( temp ) < MAX_COLS );
  222.  
  223.             strcpy( stem, temp );
  224.          } else
  225.             rc = ERROR;
  226.  
  227.       /*
  228.        * user did not enter a valid subdirectory name or search pattern.
  229.        */
  230.       } else
  231.          rc = ERROR;
  232.    }
  233.    return( rc );
  234. }
  235.  
  236.  
  237. /*
  238.  * Name:    list_and_pick
  239.  * Purpose: To show matching file names and let user pick a file
  240.  * Date:    February 13, 1992
  241.  * Passed:  dname:  directory search pattern
  242.  *          stem:   stem of directory search pattern
  243.  *          window:  pointer to current window
  244.  * Returns: return code from pick.  rc = OK, then edit a new file.
  245.  * Notes:   real work routine of this function.  save the cwd and let the
  246.  *           user search upwards or downwards thru the directory structure.
  247.  *          since we are doing DOS directory functions, we need to check the
  248.  *           return code after each DOS call for critical errors.
  249.  */
  250. int  list_and_pick( char *dname, char *stem, WINDOW *window )
  251. {
  252. int  rc;
  253. DTA  dta;               /* disk transfer address for findfirst */
  254. DIRECTORY dir;          /* contains all info for dir display */
  255. unsigned int cnt;       /* number of matching files */
  256. FTYPE *flist, *p;       /* pointer to list of matching files */
  257. char cwd[MAX_COLS];     /* save the current working directory in this buff */
  258. char dbuff[MAX_COLS];   /* temporary directory buff */
  259. char prefix[MAX_COLS];  /* directory prefix  */
  260. int  change_directory = FALSE;
  261. int  stop;
  262. int  len;
  263. int  drive;
  264.  
  265.    /*
  266.     * Some algorithms alloc the maximum possible number of files in
  267.     *  a directory, eg. 256 or 512.  Let's count the number of matching
  268.     *  files so we know egxactly how much memory to request from calloc.
  269.     *  Depending on the op system, disk media, disk format, or version of DOS,
  270.     *  the max number of files may vary, anyway, also, additionally.
  271.     */
  272.    rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN | SYSTEM |
  273.                                 SUBDIRECTORY | ARCHIVE );
  274.    if (rc != ERROR) {
  275.       for (cnt=1; (rc = my_findnext( &dta )) == OK;)
  276.          ++cnt;
  277.       flist = (FTYPE *)calloc( cnt, sizeof(FTYPE) );
  278.    } else
  279.       flist = NULL;
  280.    if (rc != ERROR && flist != NULL) {
  281.  
  282.       stop = FALSE;
  283.       /*
  284.        * If user entered drive name in search pattern, find out the drive and
  285.        *  directory stem.
  286.        */
  287.       if (stem[1] == ':') {
  288.  
  289.          /*
  290.           * If the second character of the search pattern is a ':', the
  291.           *  the first character of the pattern should be the drive.
  292.           *  Convert drive to lower case and get a numerical representation.
  293.           * CAVEAT:  In DOS v 2.x, there may be up to 63 logical drives.
  294.           *   my algorithm may blow up if the number of logical drives
  295.           *   is greater than 'Z'.
  296.           * For DOS >= 3, the number of drives is limited to 26, I think.
  297.           */
  298.          drive = stem[0];
  299.          if (drive < 'a')
  300.             drive += 32;
  301.          drive = drive - 'a' + 1;
  302.          rc = get_current_directory( dbuff, drive );
  303.          if (rc == ERROR)
  304.             stop = TRUE;
  305.          else {
  306.  
  307.             /*
  308.              * Put drive letter, ':', and '\' in front of current directory.
  309.              */
  310.             prefix[0] = (char)(drive - 1 + 'a');
  311.             prefix[1] = ':';
  312.             prefix[2] = '\\';
  313.             prefix[3] = '\0';
  314.             assert( strlen( prefix ) + strlen( dbuff ) < MAX_COLS );
  315.             strcpy( cwd, prefix );
  316.             strcat( cwd, dbuff );
  317.          }
  318.  
  319.       /*
  320.        * else get current directory from default drive
  321.        */
  322.       } else {
  323.  
  324.          /*
  325.           * 0 = default drive.
  326.           */
  327.          drive = 0;
  328.          rc = get_current_directory( dbuff, drive );
  329.          if (rc == ERROR)
  330.             stop = TRUE;
  331.          else {
  332.  
  333.             /*
  334.              * Put a '\' in front of the current directory.
  335.              */
  336.             prefix[0] = '\\';
  337.             prefix[1] = '\0';
  338.  
  339.             assert( strlen( prefix ) + strlen( dbuff ) < MAX_COLS );
  340.  
  341.             strcpy( cwd, prefix );
  342.             strcat( cwd, dbuff );
  343.          }
  344.       }
  345.  
  346.       while (stop == FALSE) {
  347.          /*
  348.           * If we had enough memory, find all matching file names.  Append
  349.           *  '\\' at the end of subdirectory names so user will know if
  350.           *  name is a directory.  Might as well find everything, because
  351.           *  i also forget subdirectory names, too.
  352.           *
  353.           * when we get here, we have already done: 1) my_findfirst and
  354.           *  my_findnext, 2) counted the number of matching files, and
  355.           *  3) allocated space.
  356.           */
  357.          p = flist;
  358.          cnt = 0;
  359.  
  360.          rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN | SYSTEM |
  361.                                  SUBDIRECTORY | ARCHIVE );
  362.          if (rc != ERROR) {
  363.  
  364.             /*
  365.              * p is pointer that walks down the file info structure.
  366.              *  save the file name, file size, and directory character,
  367.              *  if needed, for each matching file we find.
  368.              */
  369.  
  370.             assert( strlen( dta.name ) < 14 );
  371.  
  372.             strcpy( p->fname, dta.name );
  373.             p->fsize = dta.size;
  374.             if (dta.attrib & SUBDIRECTORY)
  375.                strcat( p->fname, "\\" );
  376.             for (cnt=1; (rc = my_findnext( &dta )) == OK; ) {
  377.                ++p;
  378.  
  379.                assert( strlen( dta.name ) < 14 );
  380.  
  381.                strcpy( p->fname, dta.name );
  382.                p->fsize = dta.size;
  383.                if (dta.attrib & SUBDIRECTORY)
  384.                   strcat( p->fname, "\\" );
  385.                cnt++;
  386.             }
  387.          }
  388.  
  389.          if (rc != ERROR) {
  390.             shell_sort( flist, cnt );
  391.  
  392.             /*
  393.              * figure out number of rows, cols, etc... then display dir list
  394.              */
  395.             setup_directory_window( &dir, cnt );
  396.             write_directory_list( flist, dir );
  397.  
  398.             /*
  399.              * Let user select file name or another search directory.
  400.              *  Save the choice in dbuff.  rc == OK if user selected file or dir.
  401.              */
  402.             rc = select_file( flist, stem, &dir );
  403.  
  404.             assert( strlen( flist[dir.select].fname ) < MAX_COLS );
  405.  
  406.             strcpy( dbuff, flist[dir.select].fname );
  407.          }
  408.  
  409.          /*
  410.           *  give memory back.
  411.           */
  412.          free( flist );
  413.  
  414.          if (rc == ERROR)
  415.             stop = TRUE;
  416.          else {
  417.             len = strlen( dbuff );
  418.  
  419.             /*
  420.              * If the last character in a file name is '\' then let's
  421.              *  do a dir on selected directory.  See the matching
  422.              *  else when the user selects a file.
  423.              */
  424.             if (dbuff[len-1] == '\\') {
  425.  
  426.                /*
  427.                 * Stem has subdirectory path.  dbuff has selected path.
  428.                 * Create a new dname with stem and dbuff.
  429.                 */
  430.  
  431.                assert( strlen( stem ) + strlen( dbuff ) < MAX_COLS );
  432.  
  433.                strcpy( dname, stem );
  434.                strcat( dname, dbuff );
  435.                len = strlen( dname );
  436.                strcpy( dbuff, dname );
  437.  
  438.                /*
  439.                 * The last character in dbuff is '\', because we append the
  440.                 *  '\' to every directory entry in the file list.  Replace
  441.                 *  it with a NULL char then we will have a valid path name.
  442.                 */
  443.                dbuff[len-1] = '\0';
  444.  
  445.                /*
  446.                 * now let's change to the selected subdirectory.
  447.                 */
  448.                rc = set_current_directory( dbuff );
  449.                if (rc == OK) {
  450.  
  451.                   /*
  452.                    * Every time we change directories, we need to get the
  453.                    *  current directory so we will be sure to have the
  454.                    *  correct path.
  455.                    */
  456.                   rc = get_current_directory( dbuff, drive );
  457.                   if (rc == OK) {
  458.  
  459.                      assert( strlen( prefix ) + strlen( dbuff ) < MAX_COLS );
  460.  
  461.                      strcpy( dname, prefix );
  462.                      strcat( dname, dbuff );
  463.                      change_directory = TRUE;
  464.                   }
  465.                }
  466.  
  467.                /*
  468.                 * Validate the new path and allocate memory for the
  469.                 *  matching files.
  470.                 */
  471.                if (rc == OK)
  472.                   rc = validate_path( dname, stem );
  473.                if (rc == OK) {
  474.                   rc = my_findfirst( &dta, dname, NORMAL | READ_ONLY | HIDDEN |
  475.                                   SYSTEM | SUBDIRECTORY | ARCHIVE );
  476.                   if (rc != ERROR) {
  477.                      for (cnt=1; (rc = my_findnext( &dta )) == OK;)
  478.                         ++cnt;
  479.                      flist = (FTYPE *)calloc( cnt, sizeof(FTYPE) );
  480.                   }
  481.                }
  482.                if (flist == NULL || rc == ERROR) {
  483.                   stop = TRUE;
  484.                   rc = ERROR;
  485.                }
  486.             } else {
  487.  
  488.                /*
  489.                 * user selected a file.  store fname in dname and return.
  490.                 */
  491.                rc = OK;
  492.                stop = TRUE;
  493.  
  494.                assert( strlen( stem ) + strlen( dbuff ) < MAX_COLS );
  495.  
  496.                strcpy( dname, stem );
  497.                strcat( dname, dbuff );
  498.             }
  499.          }
  500.       }
  501.  
  502.       /*
  503.        * Go back to the current directory if needed.
  504.        */
  505.       if (change_directory)
  506.          set_current_directory( cwd );
  507.       if (window != NULL)
  508.          redraw_screen( window );
  509.    } else {
  510.       /*
  511.        * out of memory
  512.        */
  513.       error( WARNING,  window != NULL ? window->bottom_line : g_display.nlines,
  514.              dir3 );
  515.       rc = ERROR;
  516.    }
  517.    return( rc );
  518. }
  519.  
  520.  
  521. /*
  522.  * Name:    setup_directory_window
  523.  * Purpose: set number of rows and cols in directory window
  524.  * Date:    February 13, 1992
  525.  * Passed:  dir: pointer to directory structure
  526.  *          cnt: number of files
  527.  * Notes:   set up stuff we need to know about how to display files.
  528.  */
  529. void setup_directory_window( DIRECTORY *dir, int cnt )
  530. {
  531. int  i;
  532. int  wid;
  533. char temp[MAX_COLS];    /* line to output */
  534.  
  535.    /*
  536.     * setup the fixed vars used in dir display.
  537.     *    dir->col =      physical upper left column of dir screen
  538.     *    dir->row =      physical upper left row or line of dir screen
  539.     *    dir->wid =      width of physical screen
  540.     *    dir->hgt =      height of physical screen
  541.     *    dir->max_cols   number of columns of files in dir screen
  542.     *    dir->max_lines  number of lines of files in each column in dir screen
  543.     *    dir->cnt        number of files in list
  544.     */
  545.    dir->col = 3;
  546.    dir->row = 5;
  547.    wid = dir->wid = 72;
  548.    dir->hgt = 16;
  549.    dir->max_cols = 5;
  550.    dir->max_lines = 9;
  551.    dir->cnt = cnt;
  552.  
  553.    /*
  554.     * Find out how many lines in each column are needed to display
  555.     *  matching files.
  556.     */
  557.    dir->lines = dir->cnt / dir->max_cols + (dir->cnt % dir->max_cols ? 1 : 0);
  558.    if (dir->lines > dir->max_lines)
  559.       dir->lines = dir->max_lines;
  560.  
  561.    /*
  562.     * Find out how many columns of file names we need.
  563.     */
  564.    dir->cols = dir->cnt / dir->lines + (dir->cnt % dir->lines ? 1 : 0);
  565.    if (dir->cols > dir->max_cols)
  566.       dir->cols = dir->max_cols;
  567.  
  568.  
  569.    /*
  570.     * Find the maximun number of file names we can display in help screen.
  571.     */
  572.    dir->avail = dir->lines * dir->cols;
  573.  
  574.    /*
  575.     * Now find the number of file names we do have on the screen.  Every
  576.     *  time we slide the "window", we have to calculate a new nfiles.
  577.     */
  578.    dir->nfiles = dir->cnt > dir->avail ? dir->avail : dir->cnt;
  579.  
  580.    /*
  581.     * A lot of times, the number of matching files will not fit evenly
  582.     *  in our help screen.  The last column on the right will be partially
  583.     *  filled, hence the variable name prow (partial row).  When there are
  584.     *  more file names than can fit on the screen, we have to calculate
  585.     *  prow every time we slide the "window" of files.
  586.     */
  587.    dir->prow = dir->lines - (dir->avail - dir->nfiles);
  588.  
  589.    /*
  590.     * Find out how many "virtual" columns of file names we have.  If
  591.     *  all the files can fit in the dir screen, there will be no
  592.     *  virtual columns.
  593.     */
  594.    if (dir->cnt < dir->avail)
  595.       dir->vcols = 0;
  596.    else
  597.       dir->vcols =  (dir->cnt - dir->avail) / dir->max_lines +
  598.                    ((dir->cnt - dir->avail) % dir->max_lines ? 1 : 0);
  599.  
  600.    /*
  601.     * Find the physical display column in dir screen.
  602.     */
  603.    dir->flist_col[0] = dir->col + 2;
  604.    for (i=1; i<dir->max_cols; i++)
  605.       dir->flist_col[i] = dir->flist_col[i-1] + 14;
  606.  
  607.    /*
  608.     * Now, draw the borders of the dir screen.
  609.     */
  610.    for (i=0; i < dir->hgt; i++) {
  611.       if (i == 0 || i == dir->hgt-1) {
  612.          memset( temp, '─', wid );
  613.          temp[wid] = '\0';
  614.          if (i == 0) {
  615.             temp[0] = '┌';
  616.             temp[wid-1] = '┐';
  617.          } else {
  618.             temp[0] = '└';
  619.             temp[wid-1] = '┘';
  620.          }
  621.       } else {
  622.          memset( temp, ' ', wid );
  623.          temp[wid] = '\0';
  624.          temp[0] = temp[wid-1] = '│';
  625.       }
  626.       s_output( temp, dir->row+i, dir->col, g_display.help_color );
  627.    }
  628.  
  629.    /*
  630.     * Write headings in help screen.
  631.     */
  632.    s_output( dir4, dir->row+1, dir->col+3, g_display.help_color );
  633.    s_output( dir5, dir->row+2, dir->col+3, g_display.help_color );
  634.    s_output( dir6, dir->row+2, dir->col+44, g_display.help_color );
  635.    s_output( dir7, dir->row+14, dir->col+8, g_display.help_color );
  636. }
  637.  
  638.  
  639. /*
  640.  * Name:    write_directory_list
  641.  * Purpose: given directory list, display matching files
  642.  * Date:    February 13, 1992
  643.  * Passed:  flist: pointer to list of files
  644.  *          dir:   directory display structure
  645.  * Notes:   blank out the previous file name and display the new one.
  646.  */
  647. void write_directory_list( FTYPE *flist, DIRECTORY dir )
  648. {
  649. FTYPE *p, *top;
  650. int  i;
  651. int  j;
  652. int  k;
  653. int  end;
  654. int  line;
  655. int  col;
  656. int  color;
  657.  
  658.    color = g_display.help_color;
  659.    top = flist;
  660.    for (i=0; i < dir.lines; ++i) {
  661.       p = top;
  662.       end = FALSE;
  663.       for (j=0; j < dir.cols; ++j) {
  664.          col = dir.flist_col[j];
  665.          line = i + dir.row + 4;
  666.  
  667.          /*
  668.           * We need to blank out all lines and columns used to display
  669.           *  files, because there may be some residue from a previous dir
  670.           */
  671.          s_output( "            ", line, col, color );
  672.          if (!end) {
  673.             s_output( p->fname, line, col, color );
  674.             p += dir.lines;
  675.             k = p - flist;
  676.             if (k >= dir.nfiles)
  677.                end = TRUE;
  678.          }
  679.       }
  680.       ++top;
  681.    }
  682. }
  683.  
  684.  
  685. /*
  686.  * Name:    select_file
  687.  * Purpose: To let user select a file from dir list
  688.  * Date:    February 13, 1992
  689.  * Passed:  flist: pointer to list of files
  690.  *          stem:  base directory
  691.  *          dir:   directory display stuff
  692.  * Notes:   let user move thru the file names with the cursor keys
  693.  */
  694. int  select_file( FTYPE *flist, char *stem, DIRECTORY *dir )
  695. {
  696. int  ch;                /* input character from user */
  697. int  func;              /* function of character input by user */
  698. int  fno;               /* index into flist of the file under cursor */
  699. int  goodkey;           /* is key a recognized function key? */
  700. int  r;                 /* current row of cursor */
  701. int  c;                 /* current column of cursor */
  702. int  offset;            /* offset into file list */
  703. int  stop;              /* stop indicator */
  704. int  stem_len;          /* stem length */
  705. int  color;             /* color of help screen */
  706. int  file_color;        /* color of current file */
  707. int  change;            /* boolean, hilite another file? */
  708. int  oldr;              /* old row */
  709. int  oldc;              /* old column */
  710. char asize[20];         /* ascii file size */
  711. char blank[20];         /* blank out file names */
  712.  
  713.    /*
  714.     * initial everything.
  715.     */
  716.    memset( blank, ' ', 12 );
  717.    blank[12] = '\0';
  718.    c = r = 1;
  719.    ch = fno = offset = 0;
  720.    color = g_display.help_color;
  721.    file_color = g_display.hilited_file;
  722.    goodkey = TRUE;
  723.    stop = FALSE;
  724.    stem_len = strlen( stem );
  725.    s_output( stem, dir->row+1, dir->col+19, color );
  726.    s_output( flist[fno].fname, dir->row+1, dir->col+19+stem_len, color );
  727.    ltoa( flist[fno].fsize, asize, 10 );
  728.    s_output( blank, dir->row+2, dir->col+19, color );
  729.    s_output( asize, dir->row+2, dir->col+19, color );
  730.    itoa( dir->cnt,  asize, 10 );
  731.    s_output( blank, dir->row+2, dir->col+57, color );
  732.    s_output( asize, dir->row+2, dir->col+57, color );
  733.    xygoto( (c-1)*14+dir->col+2, r+dir->row+3 );
  734.    hlight_line( (c-1)*14+dir->col+2, r+dir->row+3, 12, file_color );
  735.    change = FALSE;
  736.    while (stop == FALSE) {
  737.       oldr = r;
  738.       oldc = c;
  739.       ch = getkey( );
  740.       func = getfunc( ch );
  741.  
  742.       /*
  743.        * User may have redefined the Enter and ESC keys.  Make the Enter key
  744.        *  perform a Rturn in this function. Make the ESC key do an AbortCommand.
  745.        */
  746.       if (ch == RTURN)
  747.          func = Rturn;
  748.       else if (ch == ESC)
  749.          func = AbortCommand;
  750.  
  751.       switch (func) {
  752.          case Rturn       :
  753.          case NextLine    :
  754.          case BegNextLine :
  755.             stop = TRUE;
  756.             break;
  757.          case AbortCommand :
  758.             stop = TRUE;
  759.             break;
  760.          case LineUp :
  761.             if (r > 1) {
  762.                change = TRUE;
  763.                --r;
  764.             } else {
  765.                r = dir->lines;
  766.                change = TRUE;
  767.                if (offset == 0 || c > 1) {
  768.                   if (c > 1)
  769.                      --c;
  770.                } else if (dir->vcols > 0 && offset > 0 && c == 1) {
  771.                   /*
  772.                    * recalculate the dir display stuff.
  773.                    */
  774.                   offset -= dir->lines;
  775.                   recalculate_dir( dir, flist, offset );
  776.                }
  777.             }
  778.             goodkey = TRUE;
  779.             break;
  780.          case LineDown :
  781.             if (r < dir->prow) {
  782.                change = TRUE;
  783.                ++r;
  784.             } else if (r < dir->lines && c != dir->cols) {
  785.                change = TRUE;
  786.                ++r;
  787.             } else {
  788.                change = TRUE;
  789.                r = 1;
  790.                if (offset == dir->vcols * dir->lines || c < dir->cols) {
  791.                   if (c < dir->cols)
  792.                      ++c;
  793.                } else if (dir->vcols > 0 && offset < dir->vcols * dir->lines &&
  794.                          c == dir->cols) {
  795.                   offset += dir->lines;
  796.                   recalculate_dir( dir, flist, offset );
  797.                }
  798.             }
  799.             goodkey = TRUE;
  800.             break;
  801.          case CharLeft :
  802.             if (offset == 0 || c > 1) {
  803.                if (c > 1) {
  804.                   change = TRUE;
  805.                   --c;
  806.                }
  807.             } else if (dir->vcols > 0 && offset > 0 && c == 1) {
  808.                change = TRUE;
  809.  
  810.                /*
  811.                 * recalculate the dir display stuff.
  812.                 */
  813.                offset -= dir->lines;
  814.                recalculate_dir( dir, flist, offset );
  815.             }
  816.             goodkey = TRUE;
  817.             break;
  818.          case CharRight :
  819.             if (offset == dir->vcols * dir->lines || c < dir->cols) {
  820.                if (c < dir->cols) {
  821.                   change = TRUE;
  822.                   ++c;
  823.                   if (c == dir->cols) {
  824.                      if ( r > dir->prow)
  825.                         r = dir->prow;
  826.                   }
  827.                }
  828.             } else if (dir->vcols > 0 && offset < dir->vcols * dir->lines &&
  829.                          c == dir->cols) {
  830.                change = TRUE;
  831.                offset += dir->lines;
  832.                recalculate_dir( dir, flist, offset );
  833.                if (r > dir->prow)
  834.                   r = dir->prow;
  835.             }
  836.             goodkey = TRUE;
  837.             break;
  838.          case BegOfLine :
  839.             change = TRUE;
  840.             c = r = 1;
  841.             goodkey = TRUE;
  842.             break;
  843.          case EndOfLine :
  844.             change = TRUE;
  845.             r = dir->prow;
  846.             c = dir->cols;
  847.             goodkey = TRUE;
  848.             break;
  849.          case ScreenDown :
  850.             change = TRUE;
  851.             r = (c == dir->cols) ? r = dir->prow : dir->lines;
  852.             goodkey = TRUE;
  853.             break;
  854.          case ScreenUp :
  855.             change = TRUE;
  856.             r = 1;
  857.             goodkey = TRUE;
  858.             break;
  859.          default :
  860.             break;
  861.       }
  862.       if (goodkey) {
  863.          s_output( blank, dir->row+1, dir->col+19+stem_len, color );
  864.          fno = offset + (c-1)*dir->lines + (r-1);
  865.          s_output( flist[fno].fname, dir->row+1, dir->col+19+stem_len, color );
  866.          ltoa( flist[fno].fsize, asize, 10 );
  867.          s_output( blank, dir->row+2, dir->col+19, color );
  868.          s_output( asize, dir->row+2, dir->col+19, color );
  869.          xygoto( (c-1)*14+dir->col+2, r+dir->row+3 );
  870.          goodkey = FALSE;
  871.          if (change) {
  872.             hlight_line( (oldc-1)*14+dir->col+2, oldr+dir->row+3, 12, color );
  873.             hlight_line( (c-1)*14+dir->col+2, r+dir->row+3, 12, file_color );
  874.             change = FALSE;
  875.          }
  876.       }
  877.    }
  878.    dir->select = fno;
  879.    return( func == AbortCommand ? ERROR : OK );
  880. }
  881.  
  882.  
  883. /*
  884.  * Name:    recalculate_dir
  885.  * Purpose: To recalcute dir structure when cursor goes ahead or behind screen
  886.  * Date:    February 13, 1992
  887.  * Passed:  dir:    pointer to file structure
  888.  *          flist:  pointer to file structure
  889.  *          offset: number of files from beginning of flist
  890.  * Notes:   Find new number of files on the screen.  Then, find out
  891.  *           how many files names are in the last column.
  892.  */
  893. void recalculate_dir( DIRECTORY *dir , FTYPE *flist, int offset )
  894. {
  895. register int off;
  896.  
  897.    off = offset;
  898.    dir->nfiles = (dir->cnt - off) > dir->avail ? dir->avail :
  899.                 (dir->cnt - off);
  900.    dir->prow = dir->lines - (dir->avail - dir->nfiles);
  901.    write_directory_list( flist+off, *dir );
  902. }
  903.  
  904.  
  905. /*
  906.  * Name:    shell_sort
  907.  * Purpose: To sort file names
  908.  * Date:    February 13, 1992
  909.  * Passed:  flist: pointer to file structure
  910.  *          cnt:   number of files to sort
  911.  * Notes:   this implementation of Shellsort is based on the one by Robert
  912.  *           Sedgewick on page 109, _Algorithms in C_.
  913.  */
  914. void shell_sort( FTYPE *flist, int cnt )
  915. {
  916. int  i;
  917. register int j;
  918. register int inc;
  919. FTYPE temp;
  920. FTYPE *fl;
  921.  
  922.    if (cnt > 1) {
  923.       fl = flist;
  924.  
  925.       /*
  926.        * figure the increments, per Donald Knuth, _Sorting and Searching_, and
  927.        *  Robert Sedgewick, _Algorithms in C_.
  928.        */
  929.       j = cnt / 9;
  930.       for (inc=1; inc <= j; inc = 3 * inc + 1);
  931.  
  932.       /*
  933.        * now, Shellsort the directory file names.
  934.        */
  935.       for (; inc > 0; inc /= 3) {
  936.          for (i=inc; i < cnt; i++) {
  937.             j = i;
  938.             memcpy( &temp, fl+j, sizeof(FTYPE) );
  939.             while (j >= inc && memcmp( fl[j-inc].fname, temp.fname, 14 ) > 0) {
  940.                memcpy( fl+j, fl+j-inc, sizeof(FTYPE) );
  941.                j -= inc;
  942.             }
  943.             memcpy( fl+j, &temp, sizeof(FTYPE) );
  944.          }
  945.       }
  946.    }
  947. }
  948.